package com.jumi.microservice.common.core.aspect.handler;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.TableNameParser;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.jumi.microservice.common.constant.SqlCommandTypeEnum;
import com.jumi.microservice.common.core.aspect.DataTem;
import com.jumi.microservice.common.core.aspect.WebLogAspect;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.PreparedStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.*;

import static org.hibernate.param.CollectionFilterKeyParameterSpecification.PARAM_KEY;

/**
 * - - - - - - - - - - - - - - -
 * 数据拦截器
 * @author liuz
 * @date 2020/7/22 18:15
 * ToDoSomeThing
 * - - - - - - - - - - - - - - -
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "update", args = {Statement.class})})
public class DataUpdateInterceptor extends AbstractSqlParserHandler implements Interceptor {

    @Autowired
    private DruidDataSource druidDataSource;

    @Autowired
    private DataObjectUtil dataObjectUtil;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取线程名，使用线程名作为同一次操作记录
        String threadName = Thread.currentThread().getName();
        // 判断是否需要记录日志
        if (!WebLogAspect.hasThread(threadName)) {
            return invocation.proceed();
        }
        Statement statement;
        Object firstArg = invocation.getArgs()[0];
        if (Proxy.isProxyClass(firstArg.getClass())) {
            statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement");
        } else {
            statement = (Statement) firstArg;
        }
        MetaObject stmtMetaObj = SystemMetaObject.forObject(statement);
        try {
            statement = (Statement) stmtMetaObj.getValue("stmt.statement");
        } catch (Exception e) {
            // do nothing
        }
        if (stmtMetaObj.hasGetter("delegate")) {
            //Hikari
            try {
                statement = (Statement) stmtMetaObj.getValue("delegate");
            } catch (Exception ignored) {

            }
        }

        String originalSql = statement.toString();
        originalSql = originalSql.replaceAll("[\\s]+", StringPool.SPACE);
        int index = indexOfSqlStart(originalSql);
        if (index > 0) {
            originalSql = originalSql.substring(index);
        }

        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

        // 获取执行Sql
        String sql = originalSql.replace("where", "WHERE");
        // 使用mybatis-plus 工具解析sql获取表名
        Collection<String> tables = new TableNameParser(sql).tables();
        if (CollectionUtils.isEmpty(tables)) {
            return invocation.proceed();
        }
        String tableName = tables.iterator().next();
        // 使用mybatis-plus 工具根据表名找出对应的实体类
        Class<?> entityType = TableInfoHelper.getTableInfos().stream().filter(t -> t.getTableName().equals(tableName))
                .findFirst().orElse(new TableInfo(null)).getEntityType();

        if (entityType == null) {
            return invocation.proceed();
        }
        DataTem dataTem = new DataTem();
        dataTem.setTableName(tableName);
        dataTem.setEntityType(entityType);
        // 插入
        if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
            invocation.proceed();
//            RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
//            StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(statementHandler, "delegate");
            if(statementHandler!=null&&statementHandler.getParameterHandler()!=null){
                ParameterHandler p = statementHandler.getParameterHandler();
                JSONObject o = JSONObject.parseObject(JSONObject.toJSONString(p.getParameterObject()));
                dataTem.setId(o.getString("id")!=null?Long.parseLong(o.getString("id")):0L);
            }
        //获得结果集
            dataTem.setType(SqlCommandTypeEnum.INSERT.getType());
            WebLogAspect.put(threadName, dataTem);
            return 1;
        }
        // 更新
        if (SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
            try {
                // 设置sql用于执行完后查询新数据
                dataTem.setSql("SELECT * FROM " + tableName + " WHERE id in ");
                String selectSql = "SELECT * FROM " + tableName + " " + sql.substring(sql.lastIndexOf("WHERE"));
                // 查询更新前数据
//                List<?> oldData = Db.use(druidDataSource).query(selectSql, entityType);
                List<?> oldData = dataObjectUtil.getOldData(selectSql, entityType, druidDataSource);
                dataTem.setOldData(oldData);
                dataTem.setType(SqlCommandTypeEnum.UPDATE.getType());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 删除
        if (SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType())) {
            dataTem.setType(SqlCommandTypeEnum.DELETE.getType());
        }
        WebLogAspect.put(threadName, dataTem);
        return invocation.proceed();
    }

    /**
     * 获取sql语句开头部分
     *
     * @param sql ignore
     * @return ignore
     */
    private int indexOfSqlStart(String sql) {
        String upperCaseSql = sql.toUpperCase();
        Set<Integer> set = new HashSet<>();
        set.add(upperCaseSql.indexOf("SELECT "));
        set.add(upperCaseSql.indexOf("UPDATE "));
        set.add(upperCaseSql.indexOf("INSERT "));
        set.add(upperCaseSql.indexOf("DELETE "));
        set.remove(-1);
        if (CollectionUtils.isEmpty(set)) {
            return -1;
        }
        List<Integer> list = new ArrayList<>(set);
        list.sort(Comparator.naturalOrder());
        return list.get(0);
    }

}
